/** 
 * Title: test.dump.cpp
 * Description: Manually construct ast and test dump. Use sort.pas for this test.
 * Copyright: Copyright (c) 2022
 * Company: Beijing University of Posts and Telecommunications
 * @author 2019211410 王敏行
 * @date 2022/04/10 16:32 GMT+08:00
 * @version 1.0.0-beta
 */

#include "../src/dump/ast.dump.h"

// clang++ --analyze test/test.dump.cpp
// 此测试为减少代码量，复用了一些节点的指针，并不严谨
void test_dump(void)
{
    AST* ast{ nullptr };

    // PROGRAM Sort(input, output);
    IdentifierListAST* io = new IdentifierListAST{};
    io->push_back("input");
    io->push_back("output");
    ProgramParametersAST* pp = new ProgramParametersAST{ io };
    ProgramHeadingAST* proghead = new ProgramHeadingAST{ "Sort", pp };

    // CONST
    // MaxElts = 50;
    ConstantAST* elts = new ConstantAST{ ConstantAST::Type::INTEGER, "50" };
    ConstantDefinitionAST* constDef = new ConstantDefinitionAST{ "MaxElts", elts };
    ConstantDefinitionListAST* constPart = new ConstantDefinitionListAST{};
    constPart->push_back(constDef);

    // TYPE
    // IntArrType = ARRAY [1..MaxElts] OF Integer;
    SimpleTypeIdentifierAST* stdi =
        new SimpleTypeIdentifierAST{ SimpleTypeIdentifierAST::StandardType::INTEGER };
    ConstantAST* beg = new ConstantAST{ ConstantAST::Type::INTEGER, "1" };
    ConstantAST* end = new ConstantAST{ ConstantAST::Type::IDENTIFIER, "MaxElts" };
    PeriodAST* per = new PeriodAST{ beg, end };
    PeriodListAST* perList = new PeriodListAST{};
    perList->push_back(per);
    ArrayTypeAST* arrT = new ArrayTypeAST{ perList, stdi };
    TypeDefinitionAST* typdef = new TypeDefinitionAST{ "IntArrType", arrT };
    TypeDefinitionListAST* typList = new TypeDefinitionListAST{};
    typList->push_back(typdef);

    // VAR
    // i, j, tmp, size: integer;
    // arr: IntArrType;
    stdi = new SimpleTypeIdentifierAST{ SimpleTypeIdentifierAST::StandardType::INTEGER };
    IdentifierListAST* idList = new IdentifierListAST{};
    idList->push_back("i");
    idList->push_back("j");
    idList->push_back("tmp");
    idList->push_back("size");
    VariableDeclarationAST* varij = new VariableDeclarationAST{ idList, stdi };
    TypeIdentifierAST* tpid = new TypeIdentifierAST{ "IntArrType" };
    idList = new IdentifierListAST{};
    idList->push_back("arr");
    VariableDeclarationAST* ar = new VariableDeclarationAST{ idList, tpid };
    VariableDeclarationListAST* varList = new VariableDeclarationListAST{};
    varList->push_back(varij);
    varList->push_back(ar);

    // PROCEDURE ReadArr(VAR size: Integer; VAR a: IntArrType);
    idList = new IdentifierListAST{};
    idList->push_back("size");
    VariableParameterSpecificationAST* varP = new VariableParameterSpecificationAST{ idList, stdi };
    idList = new IdentifierListAST{};
    idList->push_back("a");
    ValueParameterSpecificationAST* valP = new ValueParameterSpecificationAST{ idList, tpid };
    FormalParameterListAST* forP = new FormalParameterListAST{};
    forP->push_back(varP);
    forP->push_back(valP);
    ProcedureHeadingAST* raHead = new ProcedureHeadingAST{ "ReadArr", forP };

    // BEGIN
    // size := 1;
    IdentifierAST* size =  new IdentifierAST{ "size" };
    ConstantAST* _one = new  ConstantAST{ ConstantAST::Type::INTEGER, "1" };
    UnsignedConstantAST* one = new UnsignedConstantAST{ _one };
    AssignmentStatementAST* assi = new AssignmentStatementAST{ size, one };

    // WHILE NOT eof DO BEGIN
    IdentifierAST* eof = new IdentifierAST{ "eof" };
    BinaryAST* neof = new BinaryAST{ nullptr, eof, BinaryAST::OpType::OP_NOT };

    // readln(a[size]);
    IndexExpressionAST* index = new IndexExpressionAST{ size };
    IndexExpressionListAST* ixList = new IndexExpressionListAST{};
    ixList->push_back(index);
    IndexedVariableAST* asize = new IndexedVariableAST{ "a", ixList };
    ActualParameterAST* ap = new ActualParameterAST{ asize };
    ActualParameterListAST* apl = new ActualParameterListAST{};
    apl->push_back(ap);
    ProcedureStatementAST* readln = new ProcedureStatementAST{ "readln", apl };

    // IF NOT eof THEN
    // size := size + 1
    BinaryAST* sinc = new BinaryAST{ size, one, BinaryAST::OpType::OP_ADD };
    AssignmentStatementAST* ainc = new AssignmentStatementAST{ size, sinc };
    IfStatementAST* If = new IfStatementAST{ neof, ainc, nullptr };

    // END (while)
    StatementSequenceAST* wss = new StatementSequenceAST{};
    wss->push_back(readln);
    wss->push_back(If);
    CompoundStatement* wcs = new CompoundStatement{ wss };
    WhileStatementAST* While = new WhileStatementAST{ neof, wcs };

    // END; (proc)
    StatementSequenceAST* pss = new StatementSequenceAST{};
    pss->push_back(assi);
    pss->push_back(While);
    CompoundStatement* pcs = new CompoundStatement{ pss };
    StatementPartAST* sp = new StatementPartAST{ pcs };
    BlockAST* rab = new BlockAST{ nullptr, nullptr, nullptr, nullptr, sp };
    ProcedureBodyAST* raBody = new ProcedureBodyAST{ rab };
    ProcedureDeclarationAST* ReadArr = new ProcedureDeclarationAST{ raHead, raBody };
    ProcedureFunctionDeclarationListAST* pfdl = new ProcedureFunctionDeclarationListAST{};
    pfdl->push_back(ReadArr);

    // BEGIN
    // ReadArr(size, arr);
    IdentifierAST* arr = new IdentifierAST{ "arr" };
    ActualParameterAST* apsize = new ActualParameterAST{ size };
    ActualParameterAST* aparr = new ActualParameterAST{ arr };
    ActualParameterListAST* raapl = new ActualParameterListAST{};
    raapl->push_back(apsize);
    raapl->push_back(aparr);
    ProcedureStatementAST* ras = new ProcedureStatementAST{ "ReadArr", raapl };

    // FOR i := size - 1 DOWNTO 1 DO
    IdentifierAST* i = new IdentifierAST{ "i" };
    BinaryAST* dec = new BinaryAST{ size, one, BinaryAST::OpType::OP_SUB };
    
    // FOR j := 1 TO i DO
    IdentifierAST* j = new IdentifierAST{ "j" };

    // IF arr[j] > arr[j + 1] THEN BEGIN
    IndexExpressionAST* ij = new IndexExpressionAST{ j };
    IndexExpressionListAST* ijl = new IndexExpressionListAST{};
    ijl->push_back(ij);
    IndexedVariableAST* arrj = new IndexedVariableAST{ "arr", ijl };
    BinaryAST* jp1 = new BinaryAST{ j, one, BinaryAST::OpType::OP_ADD };
    IndexExpressionAST* ij1 = new IndexExpressionAST{ jp1 };
    IndexExpressionListAST* ij1l = new IndexExpressionListAST{};
    ij1l->push_back(ij1);
    IndexedVariableAST* arrj1 = new IndexedVariableAST{ "arr", ij1l };
    BinaryAST* gt = new BinaryAST{ arrj, arrj1, BinaryAST::OpType::OP_GT };

    // tmp := arr[j];
    // arr[j] := arr[j + 1];
    // arr[j + 1] := tmp;
    IdentifierAST* tmp = new IdentifierAST{ "tmp" };
    AssignmentStatementAST* taj = new AssignmentStatementAST{ tmp, arrj };
    AssignmentStatementAST* ajaj1 = new AssignmentStatementAST{ arrj, arrj1 };
    AssignmentStatementAST* aj1t = new AssignmentStatementAST{ arrj1, tmp };
    StatementSequenceAST* ifss = new StatementSequenceAST{};
    ifss->push_back(taj);
    ifss->push_back(ajaj1);
    ifss->push_back(aj1t);
    CompoundStatement* ifcs = new CompoundStatement{ ifss };

    // END; (if)
    IfStatementAST* if2 = new IfStatementAST{ gt, ifcs, nullptr };
    ForStatementAST* for1 = new ForStatementAST{
        j, one, ForStatementAST::ForDirection::OP_DOWNTO, i, if2 };
    ForStatementAST* for2 = new ForStatementAST{
        i, dec, ForStatementAST::ForStatementAST::OP_TO, one, for1 };
    
    // FOR i := 1 TO size DO
    // writeln(arr[i])
    IndexExpressionAST* ii = new IndexExpressionAST{ i };
    IndexExpressionListAST* iil = new IndexExpressionListAST{};
    iil->push_back(ii);
    IndexedVariableAST* arri = new IndexedVariableAST{ "arr", iil };
    ActualParameterAST* apw = new ActualParameterAST{ arri };
    ActualParameterListAST* apwl = new ActualParameterListAST{};
    apwl->push_back(apw);
    ProcedureStatementAST* writeln = new ProcedureStatementAST{ "writeln", apwl };
    ForStatementAST* forlast = new ForStatementAST{ 
        i, one, ForStatementAST::ForDirection::OP_TO, size, writeln };
    
    // END.
    StatementSequenceAST* progss = new StatementSequenceAST{};
    progss->push_back(ras);
    progss->push_back(for2);
    progss->push_back(forlast);
    CompoundStatement* progcs = new CompoundStatement{ progss };
    StatementPartAST* progsp = new StatementPartAST{ progcs };
    BlockAST* progb = new BlockAST{ constPart, typList, varList, pfdl, progsp };
    ProgramBlockAST* progbody = new ProgramBlockAST{ progb };
    ProgramAST* prog = new ProgramAST{ proghead, progbody };
    ast = prog;

    ASTDump dump{};
    dump.visit(prog);
}

int main()
{
    test_dump();
    return 0;
}
